package lucene.query
{
	import flash.errors.IllegalOperationError;
	import flash.utils.describeType;
	import flash.utils.getQualifiedClassName;
	
	import lucene.search.IReader;
	import lucene.search.TermWeight;
	
	
	// Abstract
	public class Query
	{
		protected var _weight:TermWeight;
		
		public function Query(self:Query)
		{
			if (self != this) {
				throw new IllegalOperationError("Can't instantiate an abstract class directly")
			}
			
			//these functions MUST be implemented in subclasses
			var unimplemented:Array = [createWeight, score, matchedDocs, execute];
			//get the fully-qualified name the abstract class
			var abstractTypeName:String = getQualifiedClassName(Query);
			//get a list of all the methods declared by the abstract class
			//if a subclass overrides a function, declaredBy will contain the subclass name
			var selfDescription:XML = describeType(this);
			var methods:XMLList = selfDescription.method.(@declaredBy == abstractTypeName && unimplemented.indexOf(this[@name])>= 0);
			if(methods.length()> 0)
			{
				//we'll only get here if the function is still unimplemented
				var concreteTypeName:String = getQualifiedClassName(this);
				throw new IllegalOperationError("Function " + methods[0].@name + " from abstract class " + abstractTypeName + " has not been implemented by subclass " + concreteTypeName);
			}			
			
		}

		public function getBoost():Number
		{
			/**
			 * @todo implement boost
			 */
			return 1.0;
		}

		/**
		 * abstract
		 * @return lucene.search.Weight
		 */
		public function createWeight(reader:IReader):* {}

		/**
		 * abstract
		 */
		public function score(docId:int, reader:IReader):Number { return 0; }

		public function matchedDocs():Array { return new Array(); }

		public function execute(reader:IReader):void {}

		protected function _initWeight(reader:IReader):*
		{
			if(this._weight !== null) {
				return this._weight;
			}
			
			this.createWeight(reader);
			var sum:Number = this._weight.sumOfSquaredWeights();
			var queryNorm:Number = reader.getSimilarity().queryNorm(sum);
			this._weight.normalize(queryNorm);
			
		}

	}
}